package com.pdg.mcplugin.questfor.commandprocessors;

import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;

import com.pdg.mcplugin.common.ArgumentList;
import com.pdg.mcplugin.common.baseclasses.CommandProcessorBase;
import com.pdg.mcplugin.questfor.QuestFor;
import com.pdg.mcplugin.questfor.config.QuestConfig;
import com.pdg.mcplugin.questfor.config.QuestsManager;
import com.pdg.mcplugin.questfor.dataproviders.DataProvider;
import com.pdg.mcplugin.questfor.dataproviders.PlayerQuestInstance;
import com.pdg.mcplugin.questfor.dataproviders.QuestInstance;
import com.pdg.mcplugin.questfor.dataproviders.QuestKey;
import com.pdg.mcplugin.questfor.dataproviders.QuestStatus;
import com.pdg.mcplugin.questfor.dataproviders.QuestTableProvider;

public class CommandProcessor extends CommandProcessorBase<QuestFor> {

	private static final String SUBCOMMAND_LIST = "LIST";
	private static final String SUBCOMMAND_LEVELS = "LEVELS";
	private static final String COMMA = ", ";
	private static final String MESSAGE_NO_QUESTS = "No quests configured.";
	private static final String MESSAGE_TOTAL_LIST_FORMAT = "Available quests: %s";
	private static final String ERROR_NO_SUCH_QUEST_FORMAT = "There is no quest named '%s'!";
	private static final String ERROR_CANNOT_QUEST_FROM_COMMAND_LINE = "Cannot quest from command line!";
	private static final String ERROR_NO_PERMISSION_FOR_QUEST_FORMAT = "You do not have permission for quest '%s'!";
	private static final String MESSAGE_MAXIMUM_LEVEL_REACHED_FORMAT = "You have reached the maximum level for quest '%s'!";
	private static final String ERROR_NO_EMPTY_SLOT_FORMAT = "You need an empty inventory slot to receive reward for quest '%s'!";
	private static final String MESSAGE_AWARD_RECEIVED_FORMAT = "Award received for quest '%s'!";
	private static final int LEVEL_INCREASE = 1;
	private static final String ERROR_NOT_ENOUGH_XP_FORMAT = "You do not have enough XP to begin the next level of for quest '%s'!";
	private static final String MESSAGE_BEHIND_FORMAT = "Your award is directly behind you at a distance of %dm";
	private static final String MESSAGE_LEFT_FORMAT = "Your award is %d degrees left at a distance of %dm";
	private static final String MESSAGE_RIGHT_FORMAT = "Your award is %d degrees right at a distance of %dm";
	private static final String MESSAGE_AHEAD_FORMAT = "Your award is dead ahead at a distance of %dm";
	private static final double ANGLE_BEHIND = 180.0;
	private static final double ANGLE_AHEAD = 0.0;
	private static final double TWO_TIMES_PI = 2.0 * Math.PI;
	private static final int NO_AVAILABLE_SLOT = -1;
	private static final double DEFAULT_Y = 64.0;
	private static final int ZERO_TOTAL_EXPERIENCE = 0;
	private static final float ZERO_EXP = 0;
	private static final int ZERO_LEVEL = 0;
	private static final String MESSAGE_NO_QUEST_LEVELS = "You have no quest levels!";
	private static final String MESSAGE_LEVELS_FORMAT = "Quest Levels: %s";

	public CommandProcessor(QuestFor thePlugin) {
		super(thePlugin, new String[]{
				"/questfor - general help on questfor (you are here)",
				"/questfor list - lists available quests",
				"/questfor levels - shows your levels of quests and their statuses",
				"/questfor (questname) - joins/fulfills/checks on questname"
		});
	}

	@Override
	protected boolean specialProcessCommand(CommandSender theCommandSender,
			String theSubCommand, ArgumentList theArgumentList) {
		if(theSubCommand.equalsIgnoreCase(SUBCOMMAND_LIST)){
			return showQuestList(theCommandSender);
		}else if(theSubCommand.equalsIgnoreCase(SUBCOMMAND_LEVELS)){
			return showQuestLevels(theCommandSender);
		}else{
			return processQuest(theCommandSender,theSubCommand);
		}
	}

	private boolean processQuest(CommandSender theCommandSender,
			String theSubCommand) {
		if(!getPlugin().getPermissionChecker().hasQuestPermission(theCommandSender)) return true;
		if(theCommandSender instanceof Player){
			Player thePlayer = (Player)theCommandSender;
			String thePlayerName = thePlayer.getName();
			String theWorld = thePlayer.getWorld().getName();
			QuestsManager theManager = getPlugin().getQuestsManager();
			if(theManager.hasQuestConfig(theSubCommand)){
				QuestConfig theQuestConfig = theManager.getQuestConfig(theSubCommand);
				if(getPlugin().getPermissionChecker().checkPermission(thePlayer, theQuestConfig.getPermission())){
					DataProvider theDataProvider = getPlugin().getDataProvider();
					QuestTableProvider theTableProvider = theDataProvider.getQuestTableProvider();
					QuestKey theKey = new QuestKey(thePlayerName, theSubCommand, theWorld); 
					QuestInstance theInstance = theTableProvider.retrieve(theKey);
					if(theInstance==null){
						theInstance = QuestInstance.defaultInstance();
					}
					if(theInstance.getStatus() == QuestStatus.COMPLETE){
						if(theInstance.getLevel()<theQuestConfig.getMaximumQuests()){
							int theNextLevel = theInstance.getLevel()+LEVEL_INCREASE;
							int theXpCost = theQuestConfig.calculateXpCost(theNextLevel);
							if(theXpCost<=thePlayer.getTotalExperience()){
								int theNewXp = thePlayer.getTotalExperience()-theXpCost;
								thePlayer.setTotalExperience(ZERO_TOTAL_EXPERIENCE);
								thePlayer.setExp(ZERO_EXP);
								thePlayer.setLevel(ZERO_LEVEL);
								thePlayer.giveExp(theNewXp);
								theInstance.setLevel(theNextLevel);
								theInstance.setStatus(QuestStatus.PENDING);
								Random random = new Random();
								double theDirection = random.nextDouble() * TWO_TIMES_PI;
								double theDistance = theQuestConfig.calculateDistance(theNextLevel);
								double theX = thePlayer.getLocation().getX()+Math.cos(theDirection)*theDistance;
								double theZ = thePlayer.getLocation().getZ()+Math.sin(theDirection)*theDistance;
								theInstance.setX(theX);
								theInstance.setZ(theZ);
								theTableProvider.updateOrCreate(theKey, theInstance);
								
							}else{
								getPlugin().sendErrorMessage(theCommandSender, String.format(ERROR_NOT_ENOUGH_XP_FORMAT,theSubCommand));
								return true;
							}
						}else{
							getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_MAXIMUM_LEVEL_REACHED_FORMAT,theSubCommand));
							return true;
						}
					}else{
						double distance = theInstance.getDistance(thePlayer.getLocation());
						if(distance<=theManager.getThreshhold()){
							int theSlot = thePlayer.getInventory().firstEmpty();
							if(theSlot!=NO_AVAILABLE_SLOT){
								thePlayer.getInventory().setItem(theSlot, theQuestConfig.getAward());
								getPlugin().sendSuccessMessage(theCommandSender, String.format(MESSAGE_AWARD_RECEIVED_FORMAT,theSubCommand));
								theInstance.setStatus(QuestStatus.COMPLETE);
								theTableProvider.update(theKey, theInstance);
								return true;
							}else{
								getPlugin().sendWarningMessage(theCommandSender, String.format(ERROR_NO_EMPTY_SLOT_FORMAT,theSubCommand));
								return true;
							}
						}
					}
					thePlayer.setCompassTarget(new Location(thePlayer.getWorld(), theInstance.getX(), DEFAULT_Y, theInstance.getZ()));
					int theDistance = (int)Math.round(theInstance.getDistance(thePlayer.getLocation()));
					int theYaw = (int)Math.round(theInstance.getRelativeYaw(thePlayer.getLocation()));
					if(theYaw==ANGLE_BEHIND){
						getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_BEHIND_FORMAT,Math.round(theDistance)));
					}else if(theYaw<ANGLE_AHEAD){
						getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_LEFT_FORMAT,-Math.round(theYaw),Math.round(theDistance)));
					}else if(theYaw>ANGLE_AHEAD){
						getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_RIGHT_FORMAT,Math.round(theYaw),Math.round(theDistance)));
					}else{
						getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_AHEAD_FORMAT,Math.round(theDistance)));
					}
					return true;
				}else{
					getPlugin().sendErrorMessage(theCommandSender, String.format(ERROR_NO_PERMISSION_FOR_QUEST_FORMAT,theSubCommand));
					return true;
				}
			}else{
				getPlugin().sendErrorMessage(theCommandSender, String.format(ERROR_NO_SUCH_QUEST_FORMAT,theSubCommand));
				return true;
			}
		}else{
			getPlugin().sendErrorMessage(theCommandSender, ERROR_CANNOT_QUEST_FROM_COMMAND_LINE);
			return true;
		}
	}

	private boolean showQuestLevels(CommandSender theCommandSender) {
		if(!getPlugin().getPermissionChecker().hasLevelsPermission(theCommandSender)) return true;
		if(theCommandSender instanceof Player){
			Player thePlayer = (Player)theCommandSender;
			List<PlayerQuestInstance> theQuestInstances = getPlugin().getDataProvider().getQuestTableProvider().getQuestInstancesForPlayer(thePlayer);
			String theMessage = null;
			if(theQuestInstances!=null){
				Iterator<PlayerQuestInstance> iterator = theQuestInstances.iterator();
				while(iterator.hasNext()){
					PlayerQuestInstance theQuestInstance = iterator.next();
					if(theMessage==null){
						theMessage = theQuestInstance.toString();
					}else{
						theMessage += COMMA + theQuestInstance.toString();
					}
				}
			}
			if(theMessage==null){
				getPlugin().sendInformationalMessage(theCommandSender, MESSAGE_NO_QUEST_LEVELS);
			}else{
				getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_LEVELS_FORMAT,theMessage));
			}
		}
		return true;
	}

	private boolean showQuestList(CommandSender theCommandSender) {
		if(!getPlugin().getPermissionChecker().hasListPermission(theCommandSender)) return true;
		return showTotalQuestList(theCommandSender);
	}

	private boolean showTotalQuestList(CommandSender theCommandSender) {
		String theMessage = null;
		Iterator<String> iterator = getPlugin().getQuestsManager().getQuestNames().iterator();
		while(iterator.hasNext()){
			String theName = iterator.next();
			if(theMessage==null){
				theMessage = theName;
			}else{
				theMessage += COMMA + theName;
			}
		}
		if(theMessage==null){
			getPlugin().sendInformationalMessage(theCommandSender, MESSAGE_NO_QUESTS);
		}else{
			getPlugin().sendInformationalMessage(theCommandSender, String.format(MESSAGE_TOTAL_LIST_FORMAT,theMessage));
		}
		return true;
	}

}
